题解 P1357 花园

题意:小$ L $有一座环形花园,沿花园的顺时针方向,他把各个花圃编号为 $1 \sim n$。花园 $1$ 和 $n$ 是相邻的。

他的环形花园每天都会换一个新花样,但他的花园都不外乎一个规则:任意相邻 $m$ 个花圃中都只有不超过 $k$ 个 C 形的花圃,其余花圃均为 P 形的花圃。

例如,若 $n=10$ , $m=5$ , $k=3$ ,则

  • CCPCPPPPCC 是一种不符合规则的花圃。
  • CCPPPPCPCP 是一种符合规则的花圃。

请帮小 L 求出符合规则的花园种数对 $10^9+7$ 取模的结果。

$2 \leq n \le 10^{15}$,$2 \leq m \leq \min(n, 5)$,$1 \leq k \lt m$。

<

我们用一个$m$位二进制数表示后$m$个花圃的状态,$1$表示为$M$.

那么令$f[i][j]$表示由状态$i$转移到状态$j$的方案数($i$和$j$都合法,即1的个数不超过$k$)。($Floyd$矩阵)

所谓转移,是指如果$i$表示第$1$~$m$个花圃的状态,那么$j$代表第$2$~第$m+1$个花圃的状态。

那么怎么求得$f[i][j]$呢?

枚举一个长度为$m-1$的状态,在它前面加$0/1$即是$i$,在它后面加$0/1$即是$j$,在过程中判断$1$的个数会不会超过$k$即可。

由于是个环,所以实质上有$n+m$个花圃,第$n+1~n+m$个花圃就相当于第$1~m$个花圃,所以我们求的答案就是一个合法状态转移$n$次,转移回原状态的方案数之和。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#include <bits/stdc++.h>
#define int long long
#define re register
#define inf 0x3f3f3f3f
#define mod 1000000007
using namespace std;
int n,m,k,t,o[39+39],ans;
struct node{
int a[39+39][39+39];
inline void init(){
for (int i=0;i<=t;++i)
for (int j=0;j<=t;++j)
if (i==j)
a[i][i]=1;
else a[i][j]=0;
}
inline void init0(){memset(a,0,sizeof(a));}
friend node operator * (node a,node b){
node c;c.init0();
for (int i=0;i<=t;++i)
for (int k=0;k<=t;++k)
for (int j=0;j<=t;++j){
c.a[i][j]+=1ll*a.a[i][k]*b.a[k][j]%mod;
if (c.a[i][j]>mod)c.a[i][j]-=mod;
}
return c;
}
friend node operator ^ (node a,int p){
node res;
res.init();
while(p){
if(p&1) res=res*a;
p>>=1;
a=a*a;
}
return res;
}
}f;
inline int read(){
int x=0,w=0;char ch=getchar();
while (!isdigit(ch))w|=ch=='-',ch=getchar();
while (isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
return w?-x:x;
}
inline int count(int x){
int res=0;
while (x){
res+=x&1;
x>>=1;
}
return res;
}
signed main(){
n=read(),m=read(),k=read();
t=(1<<m)-1;
for (int i=0;i<=t>>1;++i){
int num=count(i);
if (num<=k){
o[i]=1,o[i<<1]=1;
f.a[i][i<<1]=1;
}
if (num+1<=k){
o[i|(1<<m-1)]=1,o[i<<1|1]=1;
f.a[i][i<<1|1]=1;
f.a[i|(1<<m-1)][i<<1]=1;
f.a[i|(1<<m-1)][i<<1|1]=1;
}
}
f=f^n;
for (int i=0;i<=t;++i)
if (o[i])
ans=(ans+f.a[i][i])>=mod?ans+f.a[i][i]-mod:ans+f.a[i][i];
cout<<ans;
return 0;
}